package com.nightpiter.view.dialogs;

import static com.nightpiter.controller.log.Logger.log;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Map;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemSelectedListener;

import com.nightpiter.R;
import com.nightpiter.controller.algorithms.MetroRouterState;
import com.nightpiter.controller.algorithms.Route;
import com.nightpiter.model.objects.HubComponent;
import com.nightpiter.model.objects.Station;
import com.nightpiter.model.objects.Time;

/**
 * StationInfoDialog class.
 * 
 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
 */
public class RouteInfoDialog extends TransparentDialog implements
		View.OnClickListener, OnItemClickListener,
		DialogInterface.OnClickListener, OnItemSelectedListener {

	/**
	 * the subscriber that would be notified about most of events
	 */
	private Subscriber subscriber;

	/**
	 * station info
	 */
	private TextView nameText, openText, closeText;

	/**
	 * routes info
	 */
	private TextView routeCountText, routeDestinationText;

	/**
	 * avaliable routes
	 */
	private Spinner routesSpinner;

	/**
	 * common buttons
	 */
	private Button toggleButton, hideButton;

	/**
	 * the station, info of which is shown
	 */
	private Station currentStation;

	/**
	 * the currentRoute shown on the screen
	 */
	private Route currentRoute;

	/**
	 * all available routes
	 */
	private Route[] routes;

	/**
	 * the other end of the route
	 */
	private Station destinationStation;

	/**
	 * mode - router or stationInfo
	 */
	private boolean routerToggle = false;

	/**
	 * indicates the router state
	 */
	private boolean routerPrepared = false;

	/**
	 * this would be shown when routerToggle is off
	 */
	private LinearLayout stationInfoLayout;

	/**
	 * this would be shown when routerToggle is on
	 */
	private LinearLayout routerInfoLayout, routerCountLayout;

	/**
	 * widget for the multiple stations dialog
	 */
	private AlertDialog stationsDialog;

	/**
	 * alert builder
	 */
	private AlertDialog.Builder stationsDialogBuilder;

	/**
	 * multiple stations list
	 */
	private ListView stationsListView;

	/**
	 * enable putting the point onto the center
	 */
	private boolean scrollToPoint = false;

	/**
	 * array of stations from the same hub as the station being processed
	 */
	private ArrayList<Station> stationsArray;

	/**
	 * adapter to use when preparing the router
	 */
	ArrayAdapter<String> emptyAdapter;

	/**
	 * @param context
	 */
	public RouteInfoDialog(Context context) {
		super(context);
	}

	/**
	 * @param context
	 * @param attrs
	 * @param inflateParams
	 */
	@SuppressWarnings("unchecked")
	public RouteInfoDialog(Context context, AttributeSet attrs,
			Map inflateParams) {
		super(context, attrs, inflateParams);
	}

	@Override
	protected void onFinishInflate() {
		super.onFinishInflate();
		init();
	}

	/**
	 * linking to layout views
	 */
	private void init() {
		this.nameText = (TextView) findViewById(R.id.text_station_name);
		this.openText = (TextView) findViewById(R.id.text_station_open);
		this.closeText = (TextView) findViewById(R.id.text_station_close);
		this.routeCountText = (TextView) findViewById(R.id.text_router_count);
		this.routeDestinationText = (TextView) findViewById(R.id.text_router_destination);

		this.hideButton = (Button) findViewById(R.id.button_hide);
		this.toggleButton = (Button) findViewById(R.id.button_toggle);

		this.stationInfoLayout = (LinearLayout) findViewById(R.id.layout_station_info);
		this.routerInfoLayout = (LinearLayout) findViewById(R.id.layout_router_info);
		this.routerCountLayout = (LinearLayout) findViewById(R.id.layout_router_count);

		this.hideButton.setOnClickListener(this);
		this.toggleButton.setOnClickListener(this);

		this.routesSpinner = (Spinner) findViewById(R.id.spinner_routes);
		this.routesSpinner.setOnItemSelectedListener(this);
		this.emptyAdapter = new ArrayAdapter<String>(getContext(),
				android.R.layout.simple_spinner_item);
		this.emptyAdapter
				.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
		this.routesSpinner.setAdapter(this.emptyAdapter);
		this.routesSpinner.setEnabled(false);

		this.stationsListView = new ListView(getContext());
		this.stationsListView.setOnItemClickListener(this);
		this.stationsListView.setAdapter(new ArrayAdapter<String>(getContext(),
				R.layout.station_row));

		this.stationsDialogBuilder = new AlertDialog.Builder(getContext());
		this.stationsDialogBuilder.setView(this.stationsListView);
		this.stationsDialogBuilder.setTitle("Multiple stations");
		this.stationsDialogBuilder.setNegativeButton("Cancel", this);

		updateView();

	}

	/**
	 * update the view
	 */
	private void updateView() {
		if (routerToggle) {
			updateRouteInfo();
		} else {
			updateStationInfo();
		}
	}

	private void updateStationInfo() {
		stationInfoLayout.setVisibility(VISIBLE);
		routerInfoLayout.setVisibility(INVISIBLE);
		routerCountLayout.setVisibility(GONE);
		this.toggleButton.setText("Search routes from this station");

		if (currentStation != null) {
			Time openTime = currentStation.getOpenTime();
			Time closeTime = currentStation.getCloseTime();
			Calendar nowCalendar = Calendar.getInstance();
			if (nowCalendar.get(Calendar.HOUR_OF_DAY) > closeTime.getMinutes()
					&& nowCalendar.get(Calendar.HOUR_OF_DAY) < openTime
							.getMinutes()) {
				closeText.setTextColor(Color.RED);
			} else {
				closeText.setTextColor(Color.WHITE);
			}

			nameText.setText(currentStation.getName());
			openText.setText(openTime.toString());
			closeText.setText(closeTime.toString());
		}
		setTextFields("", "");
	}

	private void updateRouteInfo() {
		stationInfoLayout.setVisibility(INVISIBLE);
		routerInfoLayout.setVisibility(VISIBLE);
		routerCountLayout.setVisibility(VISIBLE);
		this.toggleButton.setText("Back");

		if (!routerPrepared) {
			// log("router is not yet prepared");
			this.routesSpinner.setAdapter(emptyAdapter);
			this.routesSpinner.setEnabled(false);

		} else if (destinationStation == null) {
			log("router prepared, but destination is not selected");
			setTextFields("please select", "a destination");
			this.routesSpinner.setAdapter(emptyAdapter);
			this.routesSpinner.setEnabled(false);

		} else if (routes == null) {
			log("destination selected, but routes are not yet retrieved");
			setTextFields("searching", destinationStation.getName());
			this.routesSpinner.setAdapter(emptyAdapter);
			this.routesSpinner.setEnabled(false);

		} else if (routes.length == 0) {
			log("there are no routes to the destination");
			setTextFields("no routes to", destinationStation.getName());
			this.routesSpinner.setAdapter(emptyAdapter);
			this.routesSpinner.setEnabled(false);

		} else if (currentRoute == null) {
			// o_0
			log("WTF? seriously!");

		} else {
			log("regular view of router dialog");

			// formating time and routes
			String routesCount = "";
			if (routes.length == 1) {
				routesCount = "there is one route to";
			} else {
				routesCount = "there are " + routes.length + " routes to";
			}
			setTextFields(routesCount, destinationStation.getName());

		}
	}

	private void setTextFields(String countText, String destinationText) {
		routeCountText.setText(countText);
		routeDestinationText.setText(destinationText);
	}

	/**
	 * user has clicked a buttons
	 */
	public void onClick(View targetView) {
		if (targetView == this.hideButton) {
			hide();

		} else if (targetView == this.toggleButton) {
			routerToggle = !routerToggle;
			if (subscriber != null) {
				if (routerToggle) {
					routerPrepared = false;
					routes = null;
					this.subscriber.onRouteFromDefine(currentStation);
				} else {
					this.subscriber.onRouteSelect(null);
				}
			}
			updateView();

		}
	}

	/**
	 * user has closed multiple stations dialog
	 * 
	 * @param dialog
	 * @param button
	 */
	public void onClick(DialogInterface dialog, int button) {
		if (dialog == this.stationsDialog) {
			log("multiple stations dialog closed");
		}
	}

	/**
	 * this will fire when a route would be selected
	 */
	public void onItemSelected(AdapterView parent, View v, int position, long id) {
		if (parent == this.routesSpinner) {
			if (currentRoute != routes[position]) {
				currentRoute = routes[position];
				this.subscriber.onRouteSelect(currentRoute);
			}
		}
	}

	public void onNothingSelected(AdapterView arg0) {
	}

	/**
	 * this will fire when one of multiple stations would be selected
	 */
	public void onItemClick(AdapterView parent, View v, int position, long id) {
		if (parent == this.stationsListView) {
			internalProcessStation(stationsArray.get(position));
			this.stationsDialog.dismiss();
		}
	}

	/**
	 * a new station retrieved, process it
	 * 
	 * @param station
	 * @param scrollToPoint
	 *            enable putting the point onto the center
	 */
	public void processStation(Station station, boolean scrollToPoint) {
		if (station == null) {
			hide();
			return;
		}
		this.scrollToPoint = scrollToPoint;

		if (station.getHub() == null) {
			internalProcessStation(station);
		} else {
			ArrayList<HubComponent> componentsOfHub = station.getHub()
					.getComponents();
			HubComponent foundComponent = null;
			for (HubComponent component : componentsOfHub) {
				if (component.getStations().contains(station)) {
					foundComponent = component;
					break;
				}
			}
			if (foundComponent != null) {
				stationsArray = foundComponent.getStations();
				if (stationsArray.size() == 1) {
					internalProcessStation(station);
				} else {
					String[] stationNamesArray = new String[stationsArray
							.size()];
					for (int i = 0; i < stationNamesArray.length; i++) {
						stationNamesArray[i] = stationsArray.get(i).getName();
					}
					this.stationsListView.setAdapter(new ArrayAdapter<String>(
							getContext(), R.layout.station_row,
							stationNamesArray));
					this.stationsDialog = this.stationsDialogBuilder.show();
				}
			} else {
				log("a strange situation...");
			}
		}
	}

	/**
	 * show appropriate dialog about concrete station
	 * 
	 * @param station
	 */
	private void internalProcessStation(Station station) {
		if (!routerToggle) {
			currentStation = station;
			this.subscriber.onStationSelect(currentStation, this.scrollToPoint);
		} else {
			destinationStation = station;
			currentRoute = null;
			routes = null;
			this.subscriber.onRouteSelect(null);
			this.subscriber.onRouteToDefine(station);
		}
		super.show();
		updateView();
	}

	/**
	 * an array of routes retrieved, display info
	 * 
	 * @param routes
	 */
	public void processRoutes(Route[] routes) {
		this.routes = routes;
		if (routes != null) {
			if (routes.length > 0) {
				String[] routesInfoArray = new String[routes.length];
				for (int i = 0; i < routesInfoArray.length; i++) {
					Time time = new Time(routes[i].getTotalDistance());
					String timeStr = "";
					int roundedMinutes = time.getMinutes() + time.getMinutes()
							/ 30; // rounding
					// minuts
					int roundedHours = time.getHours();
					if (roundedMinutes == 60) {
						roundedMinutes = 0;
						roundedHours += 1;
					}

					if (roundedHours == 0) {
						if (roundedMinutes == 1)
							timeStr += " 1 minute";
						else
							timeStr += roundedMinutes + " minutes";
					} else {
						if (roundedHours == 1)
							timeStr += "1 hour";
						else
							timeStr += roundedHours + " hours";
						if (roundedMinutes != 0)
							if (roundedMinutes == 1) {
								timeStr += " 01 minute";
							} else {
								timeStr += " ";
								timeStr += Integer.toString(roundedMinutes)
										+ " minutes";
							}
					}

					String transferStr = "";
					int transfers = routes[i].getTransferHops();
					if (transfers == 0) {
						transferStr = "no transfers";
					} else if (transfers == 1) {
						transferStr = "one transfer";
					} else {
						transferStr = transfers + " transfers";
					}
					routesInfoArray[i] = timeStr + " with " + transferStr;
				}
				ArrayAdapter<String> adapter = new ArrayAdapter<String>(
						getContext(), android.R.layout.simple_spinner_item,
						routesInfoArray);
				adapter
						.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
				routesSpinner.setAdapter(adapter);
				this.routesSpinner.setEnabled(true);
				currentRoute = routes[0];
				this.subscriber.onRouteSelect(currentRoute);

			} else {
				Toast
						.makeText(getContext(), "No ways found",
								Toast.LENGTH_LONG);
			}
		} else {
			log("ERROR! null routes array retrieved");
		}
		updateView();
	}

	public void onRouterStateChanged(MetroRouterState state) {
		if (state.getAction() != MetroRouterState.Action.IDLE) {
			routerPrepared = false;
			routes = null;
			setTextFields("Searching routes", "with " + state.getPass()
					+ " transfer hops");
		} else if (state.isReady()) {
			routerPrepared = true;
			destinationStation = null;
			Toast.makeText(getContext(), "Please select a destination",
					Toast.LENGTH_LONG);
		}
		updateView();
	}

	@Override
	public void show() {
		super.show();
		if (subscriber != null) {
			this.subscriber.onStationSelect(currentStation, false);
			this.subscriber.onRouteSelect(currentRoute);
		}
	}

	@Override
	public void hide() {
		super.hide();
		currentStation = null;
		currentRoute = null;
		routes = null;
		destinationStation = null;
		routerToggle = false;
		if (subscriber != null) {
			this.subscriber.onStationSelect(null, false);
			this.subscriber.onRouteSelect(null);
			this.subscriber.onDialogHide();
		}
	}

	/**
	 * subscribes to notifications
	 * 
	 * @param subscriber
	 *            the Subscriber
	 */
	public void subscribe(Subscriber subscriber) {
		if (subscriber != null) {
			this.subscriber = subscriber;
		}
	}

	/**
	 * Subscriber interface. Should be implemented by any class that would
	 * receive notifications from this View
	 * 
	 * @author Eldar Abusalimov (eldar.abusalimov@gmail.com)
	 */
	public interface Subscriber {

		public void onRouteToDefine(Station toStation);

		public void onRouteFromDefine(Station fromStation);

		public void onRouteSelect(Route route);

		public void onStationSelect(Station station, boolean scrollToPoint);

		public void onDialogHide();

	}

}
